package com.dosgi.classloader;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 线程上下文类加载器 <br>
 * <br>
 * 用法：<br>
 *<code>
 *       ContextFinder contextFinder = new ContextFinder(parent); 			<br>                
 *       try {                                                              <br>                
 *       	//设置                                       											<br>                          
 *       	contextFinder.setContextClassLoader();                          <br>               
 *       	// your code here                                               <br>
 *       }finally{                                                          <br>                
 *       	//设置线程上下文类加载器                                    						    <br>                          
 *       	contextFinder.revertContextClassLoader();                       <br>               
 *       }                                                                  <br>                
 *</code> 
 * 
 * 
 * 
 * @author dingnate
 *
 */
public class ContextFinder extends ClassLoader {
	static final class Finder extends SecurityManager {
		public Class<?>[] getClassContext() {
			return super.getClassContext();
		}
	}

	static ClassLoader finderClassLoader;
	static Finder contextFinder;
	static {
		finderClassLoader = ContextFinder.class.getClassLoader();
		contextFinder = new Finder();
	}
    private final ConcurrentHashMap<String, Object> parallelLockMap = new ConcurrentHashMap<String, Object>();
	private static Class<ContextFinder> THIS = ContextFinder.class;

	private final ClassLoader parentContextClassLoader;
	private ClassLoader preContextClassLoader;

	public ContextFinder(ClassLoader contextClassLoader) {
		super(contextClassLoader);
		this.parentContextClassLoader = contextClassLoader != null ? contextClassLoader
				: DosgiClassLoader.BOOT_CLASSLOADER;
	}

	List<ClassLoader> basicFindClassLoaders() {
		Class<?>[] stack = contextFinder.getClassContext();
		List<ClassLoader> result = new ArrayList<ClassLoader>(1);
		ClassLoader previousLoader = null;
		for (int i = 1; i < stack.length; i++) {
			ClassLoader tmp = stack[i].getClassLoader();
			if (stack[i] != THIS && tmp != null && tmp != this) {
				if (checkClassLoader(tmp)) {
					if (previousLoader != tmp) {
						result.add(tmp);
						previousLoader = tmp;
					}
				}
				// stop at the framework classloader or the first bundle
				// classloader
				if (tmp == finderClassLoader || tmp instanceof DosgiClassLoader)
					break;
			}
		}
		return result;
	}

	private boolean checkClassLoader(ClassLoader classloader) {
		if (classloader == null || classloader == getParent())
			return false;
		for (ClassLoader parent = classloader.getParent(); parent != null; parent = parent
				.getParent())
			if (parent == this)
				return false;
		return true;
	}

	private List<ClassLoader> findClassLoaders() {
		return basicFindClassLoaders();
	}

	protected Class<?> loadClass(String name, boolean resolve)
			throws ClassNotFoundException {
		synchronized (getClassLoadingLock(name)) {
			List<ClassLoader> toConsult = findClassLoaders();
			for (Iterator<ClassLoader> loaders = toConsult.iterator(); loaders
					.hasNext();)
				try {
					return loaders.next().loadClass(name);
				} catch (ClassNotFoundException e) {
					// go to the next class loader
				}
			return parentContextClassLoader.loadClass(name);
		}
	}

	public URL getResource(String name) {
		List<ClassLoader> toConsult = findClassLoaders();
		for (Iterator<ClassLoader> loaders = toConsult.iterator(); loaders
				.hasNext();) {
			URL result = loaders.next().getResource(name);
			if (result != null)
				return result;
			// go to the next class loader
		}
		return super.getResource(name);
	}

	protected Enumeration<URL> findResources(String arg0) throws IOException {
		List<ClassLoader> toConsult = findClassLoaders();
		for (Iterator<ClassLoader> loaders = toConsult.iterator(); loaders
				.hasNext();) {
			Enumeration<URL> result = loaders.next().getResources(arg0);
			if (result != null && result.hasMoreElements())
				return result;
			// go to the next class loader
		}
		return super.findResources(arg0);
	}
	
	/**
	 * 设置线程上下文ClassLoader<br>
	 * 与revertContextClassLoader()成对使用，否则会抛异常<br>
	 * 
	 * @return
	 */
	public ClassLoader setContextClassLoader() {
		Thread currentThread = Thread.currentThread();
		preContextClassLoader = currentThread
				.getContextClassLoader();
		currentThread.setContextClassLoader(this);
		return preContextClassLoader;
	}

	/**
	 * 
	 * 还原线程上下文ClassLoader
	 * 
	 */
	public void revertContextClassLoader() {
		if (preContextClassLoader == null)
			throw new RuntimeException(
					"Can not revert\nCause by preContextClassLoader is null");
		Thread.currentThread().setContextClassLoader(preContextClassLoader);
		preContextClassLoader = null;
	}
	
	protected Object getClassLoadingLock(String className) {
		Object lock = parallelLockMap.get(className);
		if (lock != null)
			return lock;
		Object newLock = new Object();
		lock = parallelLockMap.putIfAbsent(className, newLock);
		if (lock == null)
			lock = newLock;
		return lock;
	}
}
